/**
 * 
 */
package com.populardata;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;



// CWS: breakout all ZipCodeLookup stuff into a different project / jar
//	it carries a large data file, and is independent from the Frazer domain

/**
 * This entity bean is external to the Frazer domain model.  It wraps a comprehensive data
 * collection I ganked from the web from <a href="http://www.populardata.com/">www.populardata.com</a>.
 * The veracity of this data may not be 100%, but it's fairly robust.
 * 
 *  Suggested use: fetch a zipLookup instance from a session bean via the named query "LookupZipCode"
 *  and use that to auto-populate or verify City, County, or State names.  States are provided as
 *  two-letter abbreviations.
 *  
 *  Approximate Latitude and Longitude are provided for those types of zip code that it is meaningful for.
 *  
 * @author bshirley
 *         
 */
@Entity
@NamedQueries({
	@NamedQuery(name="LookupZipCode", query="SELECT (z) FROM AddressLookup AS z WHERE z.zip=:zipcode")
	,
	@NamedQuery(name="getAddressLookupCount", query = "SELECT COUNT(z) FROM AddressLookup AS z")
	,
	@NamedQuery(name="getAllStateNames", query = "SELECT DISTINCT(state) FROM AddressLookup ORDER BY state")
	,
	@NamedQuery(name="getCityNamesForState", query = "SELECT DISTINCT(city) FROM AddressLookup WHERE state=:state ORDER BY city")
	,
	@NamedQuery(name="cityCountForState", query = "SELECT DISTINCT COUNT(city) FROM AddressLookup WHERE state=:state")
	,
	@NamedQuery(name="getAddressLookupsForState", query = "SELECT a FROM AddressLookup AS a WHERE a.state=:state ORDER BY a.city")
	,
	@NamedQuery(name="getAddressLookupsForCity", query = "SELECT a FROM AddressLookup AS a WHERE a.state=:state AND a.city=:city ORDER BY a.zip")
	,
	@NamedQuery(name="getAddressLookupsNear", query = "SELECT a FROM AddressLookup AS a WHERE a.latitude BETWEEN :lat1 AND :lat2 AND a.longitude BETWEEN :long1 AND :long2")
 
})
public class AddressLookup
	implements Serializable {



	private static final int ZIP_SIZE = 5;
	private static final int CITY_SIZE = 30; // actual max 25
	private static final int STATE_SIZE = 32;
	private static final int COUNTY_SIZE = 30; // actual max 26

	// stored as integers (thus may not appear with leading 00s)
	@Id
	@Column(length = ZIP_SIZE)
	private int zip;

	private float latitude;

	private float longitude;

	@Column(length = CITY_SIZE)
	private String city;

	@Column(length = STATE_SIZE)
	private String state;

	@Column(length = COUNTY_SIZE)
	private String county;

	@Column(length = 12)
	@Enumerated(EnumType.STRING)
	private ZipClass type;

	
	//
	// Constructors
	//
	
	public AddressLookup(int zip, float latitude, float longitude, String city,
			String state, String county, String zipType) {
		super();
		this.zip = zip;
		this.latitude = latitude;
		this.longitude = longitude;
		setCity(city);
		setState(state);
		setCounty(county);
		type = ZipClass.convertString(zipType);
	}

	public AddressLookup() {
		this(0, 0, 0, null, null, null, null);
	}

	
	
	//
	// Serializable
	//
	
	private static final long serialVersionUID = 200711L;

	public boolean equals(Object other) {
		if (getClass().isInstance(other) == false)
			return false;
		
		AddressLookup a = (AddressLookup)other;
		
		if (a.zip != zip)
			return false;
		
		if (a.latitude != latitude || a.longitude != longitude)
			return false;
		
		if (a.city.equals(city) == false)
			return false;
		
		if (a.county.equals(county) == false)
			return false;
		
		if (a.state.equals(state) == false)
			return false;
		
		if (a.type.equals(type) == false)
			return false;
		
		return true;
	}
	
	public int hashCode() {
		return zip;
	}
	
	
	//
	// Support function
	//
	

	/**
	 * Converts any string to be leading capitalized, each word is capitalized.
	 */
	public static String initialCaps(String s) {
		// needed for import support from the ALL CAPS data
		
		if (s.length() == 0)
			return s;

		s = s.substring(0, 1).toUpperCase() +
			s.substring(1).toLowerCase();

		int lastBegin = 0;
		while((lastBegin = s.indexOf(" ", lastBegin)) != -1) {
			lastBegin++;
			if (lastBegin >= s.length())
				break;
			
			s = s.substring(0, lastBegin) +
				s.substring(lastBegin, lastBegin+1).toUpperCase() +
				s.substring(lastBegin+1).toLowerCase();
		}
		return s;
	}

	//
	// Accessors
	//
	
	public int getZip() {
		return zip;
	}

	public float getLatitude() {
		return latitude;
	}

	public float getLongitude() {
		return longitude;
	}

	public String getCity() {
		return city;
	}

	public String getState() {
		return state;
	}

	public String getCounty() {
		return county;
	}

	public ZipClass getType() {
		return type;
	}

	public void setCity(String cityName) {
		if (cityName == null)
			cityName = "";
		
		if (cityName.length() > CITY_SIZE)
			throw new IllegalArgumentException("Parameter exceeded expected length (" +
					CITY_SIZE + "/" + cityName.length() + ": " + cityName + ")");
		
		city = cityName;
	}

	public void setState(String stateName) {
		if (stateName == null)
			stateName = "";
		
		if (stateName.length() > STATE_SIZE)
			throw new IllegalArgumentException("Parameter exceeded expected length (" +
					STATE_SIZE + "/" + stateName.length() + ": " + stateName + ")");
		
		state = stateName;
	}

	public void setCounty(String countyName) {
		if (countyName == null)
			countyName = "";
		
		if (countyName.length() > COUNTY_SIZE)
			throw new IllegalArgumentException("Parameter exceeded expected length (" +
					COUNTY_SIZE + "/" + countyName.length() + ": " + countyName + ")");

		county = countyName;
	}

	

	
	
	
}
